// // 浏览器插件 content inject background 之间进行交流的库
import EventEmitter from 'events'
// import shortid from 'shortid'
import $const from './const'

let shortid = ()=>`${Date.now()}`.slice(2) + Math.random().toString(16).slice(2)

const keys = $const.messageKey

// // 当前环境
let isPopup = typeof chrome === 'object' && !!chrome?.extension?.getBackgroundPage && !!document.querySelector('#app') || false
let isBackground = !isPopup && typeof chrome === 'object' && !!chrome?.extension?.getBackgroundPage || false
let isContent = !isBackground && !isPopup
try{
    chrome.runtime.sendMessage('\0')
}catch(err){
    isContent = false
}
let isInject = !isBackground && !isPopup && !isContent && (typeof chrome === 'undefined' || !!chrome?.runtime?.sendMessage) || false

// currentKey
let current = isBackground?keys.background:(
    isPopup?keys.popup: (
        isContent?keys.content:keys.inject
    )
)

let debug = (msg, ...rest)=>{
    console.debug(`[${current}] ${msg}`, ...rest)
}

debug(`isBackground: ${isBackground}
isPopup: ${isPopup}
isContent: ${isContent}
isInject: ${isInject}`)

class P {
    constructor(to, cmd, data){
        this.to = to
        this.cmd = cmd
        this.data = data
        this.from = current
        // 抵达都会调用cmd调用，但如果是reply 就不处理了
        this.isReply = false
        this.id = shortid()
    }
}

class M extends EventEmitter{
    constructor(){
        super()
        // cmd callback
        this.cmdFuncs = {}
        // listening, reply
        if(isBackground){
            chrome.runtime.onMessage.addListener((p, sender, sendRes)=>{
                this.handleP(p).then(res=>{
                    sendRes(res)
                })
                return true
            })
        }else if(isContent || isInject){
            window.addEventListener('message', async e=>{
                let p = e.data
                await this.handleP(p)
            })
        }
    }

    async handleP(p){
        if(p.id){
            // 仅仅处理非自己发送或代理转发的p
            if(p.from !== current && p.proxy !== current){
                debug(`received(from: ${p.from}, to: ${p.to}, id: ${p.id}): `, p)
                if(p.to === current){
                    // 抵达，处理
                    debug(`handleP(cmd: ${p.cmd})`)
                    let res
                    if(p.isReply){
                        res = p.data
                    }else{
                        // 添加错误处理协议
                        if(!this.cmdFuncs?.[p?.cmd]){
                            res = $const.cmdRet.error.noCMD
                        }else{
                            try{
                                let ret = await this.cmdFuncs?.[p?.cmd]?.(p)
                                res = $const.cmdRet.succeed(ret)
                            }catch(err){
                                console.error('local error', err)
                                if(err.code){
                                    res = err
                                }else{
                                    res = $const.cmdRet.error.unkonwn(err.message || err.msg)
                                }
                            }
                        }
                    }
                    debug(`res: `, res)
                    this.emit(p.id, res)
                    return res
                }else{
                    // 不是送给自己的，中转
                    // redirect
                    debug(`redirectP`)
                    p.proxy = current
                    let res = await this._send(p)
                    // 返回
                    let newP = new P(p.from, p.cmd, res)
                    newP.id = p.id
                    newP.proxy = current
                    newP.from = p.to
                    newP.isReply = true
                    return this._send(newP)
                }
            }
        }
    }

    _send(p){
        return new Promise(r=>{
            if(isInject){
                window.postMessage(p)
                this.once(p.id, r)
            }else if(isContent){
                if(p.to === keys.inject){
                    window.postMessage(p)
                    this.once(p.id, r)
                }else if(p.to === keys.background){
                    chrome.runtime.sendMessage(p, r)
                }
            }else if(isPopup){
                if(p.to === keys.background){
                    chrome.runtime.sendMessage(p, r)
                }
            }
        })
    }
    send(p){
        // 添加错误处理
        return this._send(p).then(res=>{
            if(res.code === 0){
                return res.data
            }else{
                throw res
            }
        })
    }

    background(cmd, data){
        return this.send(new P(keys.background, cmd, data))
    }
    content(cmd, data){
        return this.send(new P(keys.content, cmd, data))
    }
    inject(cmd, data){
        return this.send(new P(keys.inject, cmd, data))
    }

    cmd(cmd, func){
        this.cmdFuncs[cmd] = func
    }
}

export default new M